package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

/**
 * @author AlexBob
 * @date 2019/11/15 15:43
 */
@Log4j2
@Component("AGREEMENT_POINT_RULE")
public class AgreementRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "AgreementMrr";
    private final Map<String, BigDecimal> agreementsMap = new Hashtable<>(100);
    private List<AgreementRulesParams> params;
    private Boolean isShowMRR = true;

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        var mono1 = this.r2dbcEntityTemplate.getDatabaseClient().sql("SELECT company_id as id,sum(bill_amount) as amount " +
                "FROM cw_agreements_view WHERE tenant_id=:tenantId GROUP BY company_id")
                .bind("tenantId", gradeRuleInterceptorRegistration.getRule().getTenantId())
                .fetch().all()
                .doOnNext(stringObjectMap -> this.agreementsMap.put(String.valueOf(stringObjectMap.get("id")),
                        (BigDecimal) stringObjectMap.get("amount")));
        var mono2 = this.r2dbcEntityTemplate.getDatabaseClient().sql("SELECT dict_value as isShowMRR " +
                "FROM cw_dict_data_view WHERE tenant_code=:tenantId and dict_label='isShowMRR' ")
                .bind("tenantId", gradeRuleInterceptorRegistration.getRule().getTenantId())
                .fetch().first()
                .doOnNext(map -> this.isShowMRR = (Boolean) map.get("isShowMRR"));
        return Flux.concatDelayError(mono1, mono2)
                .doFinally(r -> this.params = super.objectMapper
                        .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(),
                                new TypeReference<>() {
                                }))
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        try {
            BigDecimal amount = BigDecimal.ZERO;
            int point = 0;
            JsonNode companyNode = ticket.get("company");
            if (!ObjectUtils.isEmpty(companyNode) && !companyNode.isNull()) {

                amount = this.agreementsMap.getOrDefault(ticket.get("company").get("id").asText(), BigDecimal.ZERO);
                for (AgreementRulesParams item : params) {
                    if (!ObjectUtils.isEmpty(item.getMinAmount()) || !ObjectUtils.isEmpty(item.getMaxAmount())) {
                        if (ObjectUtils.isEmpty(item.getMinAmount())) {
                            if (amount.doubleValue() <= item.getMaxAmount()) {
                                point = item.getPoints();
                                break;
                            }
                        } else if (ObjectUtils.isEmpty(item.getMaxAmount())) {
                            if (amount.doubleValue() >= item.getMinAmount()) {
                                point = item.getPoints();
                                break;
                            }
                        } else {
                            if (amount.doubleValue() <= item.getMaxAmount() && amount.doubleValue() >= item.getMinAmount()) {
                                point = item.getPoints();
                                break;
                            }
                        }
                    }
                }
            }
            return Mono.just(PointResult.withDefault().point(point)
                    .detail(PointResult.Detail.with(point, isShowMRR ? Map.of("companyBillAmount", amount) : null, RULE_KEY)));
        } catch (Exception e) {
            return Mono.error(e);
        }

    }


    @Data
    private static class AgreementRulesParams {
        private Double minAmount;
        private Double maxAmount;
        private Integer points;
        private boolean selected;
    }

}
